/*
 * SceneParser.cpp
 *
 *  Created on: 2010-09-18
 *      Author: michal
 */
#include <cstdio>
#include <cstring>

#include "SceneParser.h"

Scene * SceneParser::ParseFromFile(const char * filename)
{
	_file = fopen(filename, "r");
	if(_file == NULL)
	{
		return NULL;
	}

	fscanf(_file, "%s", _buffer);
	if(_strcmpi(_buffer, "scene") != 0)
	{
		return NULL;
	}

	_scene = new Scene;

	while(1)
	{
		fscanf(_file, "%s", _buffer);

		if(!_strcmpi(_buffer, "end"))
			break;

		if(!_strcmpi(_buffer, "general"))
			ParseGeneral();
		else if(!_strcmpi(_buffer, "light"))
			ParseLight();
		else if(!_strcmpi(_buffer, "material"))
			ParseMaterial();
		else if(!_strcmpi(_buffer, "sphere"))
			ParseSphere();
		else if(!_strcmpi(_buffer, "plane"))
			ParsePlane();
		else if(!_strcmpi(_buffer, "metaballgroup"))
					ParseMetaballGroup();
		else if(!_strcmpi(_buffer, SINGLE_LINE_COMMENT))
			fgets(_buffer, PARSER_BUFFER_LENGTH, _file);
		else if(!_strcmpi(_buffer, MULTI_LINE_COMMENT_START))
			ParseMultilineComment();
		else
			break;
	}


	fclose(_file);

	return _scene;
}

void SceneParser::ParseMultilineComment()
{
	while(1)
	{
		fscanf(_file, "%s", _buffer);
		if(!_strcmpi(_buffer, MULTI_LINE_COMMENT_END))
			break;
	}
}

void SceneParser::ParseGeneral()
{
	while(1)
	{
		fscanf(_file, "%s", _buffer);

		if(!_strcmpi(_buffer, "end"))
			break;

		if(!_strcmpi(_buffer, "ambient"))
		{
			Color color;
			fscanf(_file, "%f %f %f", &color.R, &color.G, &color.B);
			_scene->SetAmbientColor(color);
		}
		else if(!_strcmpi(_buffer, "resolution"))
		{
			fscanf(_file, "%d %d", &_parsedScreenWidth, &_parsedScreenHeight);
			_scene->GetEditableCamera().SetResolution(_parsedScreenWidth, _parsedScreenHeight);
		}
		else if(!_strcmpi(_buffer, "show_progress"))
		{
			int showProgress;
			fscanf(_file, "%d", &showProgress);
			_parsedShowProgress = showProgress > 0;
		}
		else if(!_strcmpi(_buffer, SINGLE_LINE_COMMENT))
			fgets(_buffer, PARSER_BUFFER_LENGTH, _file);
		else if(!_strcmpi(_buffer, MULTI_LINE_COMMENT_START))
			ParseMultilineComment();
		else
			break;
	}
}

void SceneParser::ParseLight()
{
	Light * light = _scene->CreateLight();
	while(1)
	{
		fscanf(_file, "%s", _buffer);

		if(!_strcmpi(_buffer, "end"))
			break;

		if(!_strcmpi(_buffer, "position"))
		{
			Vec3 pos;
			fscanf(_file, "%f %f %f", &pos.x, &pos.y, &pos.z);
			light->_position = pos;
		}
		else if(!_strcmpi(_buffer, "diffuse"))
		{
			Color color;
			fscanf(_file, "%f %f %f", &color.R, &color.G, &color.B);
			light->_diffuse = color;
		}
		else if(!_strcmpi(_buffer, "specular"))
		{
			Color color;
			fscanf(_file, "%f %f %f", &color.R, &color.G, &color.B);
			light->_specular = color;
		}
		else if(_buffer[0] != '#')
			break;
	}
}

void SceneParser::ParseMaterial()
{
	Material * material = _scene->CreateMaterial();
	while(1)
	{
		fscanf(_file, "%s", _buffer);

		if(!_strcmpi(_buffer, "end"))
			break;

		if(!_strcmpi(_buffer, "name"))
		{
			fscanf(_file, "%s", _buffer);
			_materialMap[_buffer] = material;
		}
		else if(!_strcmpi(_buffer, "ambient"))
		{
			Color color;
			fscanf(_file, "%f %f %f", &color.R, &color.G, &color.B);
			material->_ambient = color;
		}
		else if(!_strcmpi(_buffer, "diffuse"))
		{
			Color color;
			fscanf(_file, "%f %f %f", &color.R, &color.G, &color.B);
			material->_diffuse = color;
		}
		else if(!_strcmpi(_buffer, "specular"))
		{
			Color color;
			fscanf(_file, "%f %f %f", &color.R, &color.G, &color.B);
			material->_specular = color;
		}
		else if(!_strcmpi(_buffer, "specPower"))
		{
			fscanf(_file, "%f", &material->_specularPower);
		}
		else if(!_strcmpi(_buffer, "reflectance"))
		{
			fscanf(_file, "%f", &material->_reflectance);
		}
		else if(!_strcmpi(_buffer, "transparency"))
		{
			fscanf(_file, "%f", &material->_transparency);
		}
		else if(_buffer[0] != '#')
			break;
	}
}

void SceneParser::ParseSphere()
{
	Sphere * sphere = _scene->CreateSphere();

	while(1)
	{
		fscanf(_file, "%s", _buffer);

		if(!_strcmpi(_buffer, "end"))
			break;

		if(!_strcmpi(_buffer, "position"))
		{
			Vec3 pos;
			fscanf(_file, "%f %f %f", &pos.x, &pos.y, &pos.z);
			sphere->_center = pos;
		}
		else if(!_strcmpi(_buffer, "radius"))
		{
			fscanf(_file, "%f", &sphere->_radius);
		}
		else if(!_strcmpi(_buffer, "material"))
		{
			fscanf(_file, "%s", _buffer);
			sphere->_material = *(_materialMap[_buffer]);
		}
		else if(_buffer[0] != '#')
			break;
	}

}

void SceneParser::ParsePlane()
{
	Plane * plane = _scene->CreatePlane();
	while(1)
	{
		fscanf(_file, "%s", _buffer);

		if(!_strcmpi(_buffer, "end"))
			break;

		if(!_strcmpi(_buffer, "point"))
		{
			Vec3 point;
			fscanf(_file, "%f %f %f", &point.x, &point.y, &point.z);
			plane->_point = point;
		}
		else if(!_strcmpi(_buffer, "normal"))
		{
			Vec3 normal;
			fscanf(_file, "%f %f %f", &normal.x, &normal.y, &normal.z);
			plane->_normal = normal;
		}
		else if(!_strcmpi(_buffer, "material"))
		{
			fscanf(_file, "%s", _buffer);
			plane->_material = *(_materialMap[_buffer]);
		}
		else if(_buffer[0] != '#')
			break;
	}
}

void SceneParser::ParseMetaballGroup()
{
	MetaballGroup * group = _scene->CreateMetaballGroup();

	bool parsingBall = false;
	Metaball * currBall = NULL;

	while(1)
	{
		fscanf(_file, "%s", _buffer);

		if(!_strcmpi(_buffer, "end"))
		{
			if(parsingBall)
			{
				currBall = NULL;
				parsingBall = false;
			}
			else
				break;
		}
		else if(!_strcmpi(_buffer, "metaball") && !parsingBall)
		{
			parsingBall = true;
			currBall = group->CreateMetaball();
		}
		else if(!_strcmpi(_buffer, "position") && parsingBall)
		{
			Vec3 pos;
			fscanf(_file, "%f %f %f", &pos.x, &pos.y, &pos.z);
			currBall->_center = pos;
		}
		else if(!_strcmpi(_buffer, "radius") && parsingBall)
		{
			fscanf(_file, "%f", &currBall->_radius);
		}
		else if(!_strcmpi(_buffer, "material") && parsingBall)
		{
			fscanf(_file, "%s", _buffer);
			currBall->_material = *(_materialMap[_buffer]);
		}
		else if(_buffer[0] != '#')
			break;
	}

}
